home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3.2
/
Ham Radio Version 3.2 (Chestnut CD-ROMs)(1993).ISO
/
packet
/
n17jsrc
/
nntpcli.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-19
|
20KB
|
744 lines
/*
* Client routines for Network News Tranfer Protocol ala RFC977
*
* Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*
* Changes copyright 1990 Bernie Roehl, All Rights Reserved.
* Permission granted for non-commercial copying and use, provided
* this notice is retained.
*
* Revision history:
*
* May 11, 1990 - br checked for invalid chars in news filenames
*
* May 10, 1990 - br changed date stamp in 'From ' lines to
* seconds since GMT (to make parsing and expiry easier)
*
* May 9, 1990 - br added locking of nntp.dat and history files,
* second parameter to NNTP DIR, fixed bug in updating of
* nntp.dat
*
* early May, 1990 -- br added NNTP TRACE, NNTP DIR,
* server-specific newsgroups and connection windows,
* locking of newsgroup files using mlock() and rmlock(),
* date stamping of 'From ' lines, increased stack space,
* updating of nntp.dat only on successful sessions.
*
* July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
*
*/
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include <sys/timeb.h>
#include <ctype.h>
#include <string.h> /* for strchr() */
#ifdef __TURBOC__
#include <dir.h>
#endif
#include "global.h"
#include "timer.h"
#include "cmdparse.h"
#include "commands.h"
#include "socket.h"
#include "usock.h"
#include "netuser.h"
#include "proc.h"
#include "smtp.h"
#include "files.h"
#define NNTPMAXLEN 512
static struct nntpservers {
struct timer nntpcli_t;
char *name;
char *groups;
int lowtime, hightime; /* for connect window */
struct nntpservers *next;
};
#define NULLNNTP (struct nntpservers *)NULL
#define MAXGROUPDIRS 10
static struct grouploc {
char *prefix; /* e.g. comp, rec, net, talk, alt ... */
char *directory; /* directory where these groups should be */
} groupdirs[MAXGROUPDIRS] = { NULL, NULL };
struct nntpservers *Nntpservers = NULLNNTP;
static char *Nntpgroups = NULLCHAR;
static unsigned short nntptrace = 1;
static int nntpquiet = 0;
static char *News_spool = NULL;
static int np_all = 0; /* non-zero if Newsdir is a malloc'ed space */
static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
static void nntptick __ARGS((void *tp));
static void nntp_job __ARGS((int i1,void *tp,void *v1));
static int gettxt __ARGS((int s,FILE *fp));
static int getreply __ARGS((int s));
static int getarticle __ARGS((int s,char *msgid));
static int dogroups __ARGS((int argc,char *argv[],void *p));
static int doadds __ARGS((int argc,char *argv[],void *p));
static int dodrops __ARGS((int argc,char *argv[],void *p));
static int dokicks __ARGS((int argc,char *argv[],void *p));
static int dolists __ARGS((int argc,char *argv[],void *p));
static int donntrace __ARGS((int argc,char *argv[],void *p));
static int donnquiet __ARGS((int argc,char *argv[],void *p));
static int dondir __ARGS((int argc,char *argv[],void *p));
/* Tracing levels:
0 - no tracing
1 - serious errors reported
2 - transient errors reported
3 - session progress reported
4 - actual received articles displayed
*/
static struct cmds Nntpcmds[] = {
"addserver", doadds, 0, 3,
"nntp addserver <nntpserver> <interval>",
"directory", dondir, 0, 0, NULLCHAR,
"dropserver", dodrops, 0, 2,
"nntp dropserver <nntpserver>",
"groups", dogroups, 0, 0, NULLCHAR,
"kick", dokicks, 0, 2,
"nntp kick <nntpserver>",
"listservers", dolists, 0, 0, NULLCHAR,
"quiet", donnquiet, 0, 0, NULLCHAR,
"trace", donntrace, 0, 0, NULLCHAR,
NULLCHAR,
};
int
donntp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Nntpcmds,argc,argv,p);
}
static int
doadds(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct nntpservers *np;
for(np = Nntpservers; np != NULLNNTP; np = np->next)
if(stricmp(np->name,argv[1]) == 0)
break;
if (np == NULLNNTP) {
np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
np->name = strdup(argv[1]);
np->next = Nntpservers;
Nntpservers = np;
np->groups = NULLCHAR;
np->lowtime = np->hightime = -1;
np->nntpcli_t.func = nntptick; /* what to call on timeout */
np->nntpcli_t.arg = (void *)np;
}
if (argc > 3) {
int i;
if (np->groups == NULLCHAR) {
np->groups = mallocw(NNTPMAXLEN);
*np->groups = '\0';
}
for (i = 3; i < argc; ++i) {
if (isdigit(*argv[i])) {
int lh, ll, hh, hl;
sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
np->lowtime = lh * 100 + ll;
np->hightime = hh * 100 + hl;
} else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
tprintf("Group list too long! Group '%s' ignored!\n", argv[i]);
else { /* it's a group, and it fits... add it to list */
if (*np->groups != '\0')
strcat(np->groups, ",");
strcat(np->groups, argv[i]);
}
}
if (*np->groups == '\0') { /* No groups specified? */
free(np->groups);
np->groups = NULLCHAR;
}
}
/* set timer duration */
set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
start_timer(&np->nntpcli_t); /* and fire it up */
return 0;
}
static int
dodrops(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct nntpservers *np, *npprev = NULLNNTP;
for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
if(stricmp(np->name,argv[1]) == 0) {
stop_timer(&np->nntpcli_t);
free(np->name);
if (np->groups)
free(np->groups);
if(npprev != NULLNNTP)
npprev->next = np->next;
else
Nntpservers = np->next;
free((char *)np);
return 0;
}
tprintf("No such server enabled.\n");
return 0;
}
static int
dolists(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct nntpservers *np;
for(np = Nntpservers; np != NULLNNTP; np = np->next) {
char tbuf[80];
if (np->lowtime != -1 && np->hightime != -1)
sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
else
tbuf[0] = '\0';
tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
read_timer(&np->nntpcli_t) /1000L,
dur_timer(&np->nntpcli_t) /1000L,
tbuf, np->groups ? np->groups : "");
}
return 0;
}
static int donntrace(argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setshort(&nntptrace,"NNTP tracing",argc,argv);
}
static int donnquiet(argc, argv, p)
int argc;
char *argv[];
void *p;
{
return setbool(&nntpquiet,"NNTP quiet",argc,argv);
}
static int dondir(argc, argv, p)
int argc;
char *argv[];
void *p;
{
if (argc < 2) {
int i;
tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
tprintf("control: %s\n", Newsdir);
for (i = 0; i < MAXGROUPDIRS; ++i)
if (groupdirs[i].prefix)
tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
} else {
char *p;
if ((p = strchr(argv[1], '=')) != NULLCHAR) { /* set a groupdir */
int i;
*p++ = '\0';
for (i = 0; i < MAXGROUPDIRS; ++i)
if (groupdirs[i].prefix)
if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
if (groupdirs[i].directory) {
free(groupdirs[i].directory);
groupdirs[i].directory = NULLCHAR;
}
if (*p == '\0') {
free(groupdirs[i].prefix);
groupdirs[i].prefix = NULLCHAR;
} else
groupdirs[i].directory = strdup(p);
return 0;
}
if (*p == '\0') /* trashing a group that's not there */
return 0;
for (i = 0; i < MAXGROUPDIRS; ++i){
if (groupdirs[i].prefix == NULLCHAR) {
groupdirs[i].prefix = strdup(argv[1]);
if (groupdirs[i].directory) {
free(groupdirs[i].directory);
groupdirs[i].directory = NULL;
}
groupdirs[i].directory = strdup(p);
return 0;
}
}
tprintf("Directory table full\n");
}
else { /* no '=', so just set default */
if (News_spool)
free(News_spool);
News_spool = strdup(argv[1]);
}
if (argc > 2) { /* they specified a newsdir as well */
if (np_all)
free(Newsdir);
Newsdir = strdup(argv[2]);
np_all = 1;
}
}
return 0;
}
static int
dokicks(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct nntpservers *np;
for(np = Nntpservers; np != NULLNNTP; np = np->next)
if(stricmp(np->name,argv[1]) == 0) {
/* If the timer is not running, the timeout function has
* already been called and we don't want to call it again.
*/
if(run_timer(&np->nntpcli_t)) {
stop_timer(&np->nntpcli_t);
}
nntptick((void *)np);
return 0;
}
tprintf("No such server enabled.\n");
return 1;
}
static int
dogroups(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int i;
if(argc < 2) {
if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
tprintf("All groups are currently enabled.\n");
else
tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
return 0;
}
if(Nntpgroups == NULLCHAR)
Nntpgroups = mallocw(NNTPMAXLEN);
*Nntpgroups = '\0';
for(i=1; i < argc; ++i) {
if(i > 1)
strcat(Nntpgroups,",");
strcat(Nntpgroups,argv[i]);
}
return 0;
}
/* This is the routine that gets called every so often to connect to
* NNTP servers.
*/
static void
nntptick(tp)
void *tp;
{
newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
}
static void
nntp_job(i1,tp,v1)
int i1;
void *tp, *v1;
{
FILE *fp, *tmpf;
int s = -1, i;
/* long pos; */
struct tm *ltm;
time_t t;
int now;
struct nntpservers *np = (struct nntpservers *) tp;
struct sockaddr_in fsocket;
char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
if (nntptrace >= 3)
tprintf("NNTP daemon entered, target = %s\n",np->name);
if(availmem() < Memthresh){
if (nntptrace >= 2)
tprintf("NNTP daemon quit -- low memory\n");
/* Memory is tight, don't do anything */
start_timer(&np->nntpcli_t);
return;
}
time(&t); /* more portable than gettime() */
ltm = localtime(&t);
now = ltm->tm_hour * 100 + ltm->tm_min;
if (np->lowtime < np->hightime) { /* doesn't cross midnight */
if (now < np->lowtime || now >= np->hightime) {
if (nntptrace >= 3)
tprintf("NNTP window to '%s' not open\n", np->name);
start_timer(&np->nntpcli_t);
return;
}
} else {
if (now < np->lowtime && now >= np->hightime) {
if (nntptrace >= 3)
tprintf("NNTP window to '%s' not open\n", np->name);
start_timer(&np->nntpcli_t);
return;
}
}
fsocket.sin_addr.s_addr = resolve(np->name);
if(fsocket.sin_addr.s_addr == 0) { /* No IP address found */
if (nntptrace >= 2)
tprintf("NNTP can't resolve host '%s'\n", np->name);
/* Try again later */
start_timer(&np->nntpcli_t);
return;
}
fsocket.sin_family = AF_INET;
fsocket.sin_port = IPPORT_NNTP;
s = socket(AF_INET,SOCK_STREAM,0);
sockmode(s,SOCK_ASCII);
if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
cp = sockerr(s);
log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
cp != NULLCHAR ? cp : "");
if (nntptrace >= 2)
tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
cp != NULLCHAR ? cp : "");
goto quit;
}
/* Eat the banner */
i = getreply(s);
if(i == -1 || i >= 400) {
log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
if (nntptrace >= 1)
tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
goto quit;
}
if (mlock(Newsdir, "nntp")) {
if (nntptrace >= 2)
tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
goto quit;
}
sprintf(buf,"%s/nntp.dat",Newsdir);
if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
buf);
if (nntptrace >= 1)
tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
rmlock(Newsdir, "nntp");
goto quit;
}
rewind(fp);
/* for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
if((cp = strchr(buf,' ')) == NULLCHAR)
continue; /* something wrong with this line, skip it */
*cp = '\0';
if(stricmp(buf,np->name) == 0) {
rip(cp+1);
lastdate = strdup(cp+1);
break;
}
}
fclose(fp);
rmlock(Newsdir, "nntp");
if(lastdate == NULLCHAR)
lastdate = strdup("700101 000000");
/* snapshot the time for use later in re-writing nntp.dat */
time(&t);
ltm = localtime(&t);
/* Get a list of new message-id's */
if (np->groups) {
if (nntptrace >= 3)
tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
} else {
if (nntptrace >= 3)
tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
}
free(lastdate);
/* Get the response */
if((i = getreply(s)) != 230) { /* protocol error */
log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
if (nntptrace >= 1)
tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
goto quit;
}
if((tmpf = tmpfile()) == NULLFILE) {
if (nntptrace >= 1)
tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
goto quit;
}
if(gettxt(s,tmpf) == -1) {
log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
if (nntptrace >= 1)
tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
fclose(tmpf);
goto quit;
}
/* Open the history file */
if (mlock(Newsdir, "history")) {
if (nntptrace >= 1)
tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
fclose(tmpf);
goto quit;
}
sprintf(buf,"%s/history",Newsdir);
if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
if (nntptrace >= 1)
tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
fclose(tmpf);
goto quit;
}
/* search through the history file for matching message id's */
rewind(tmpf);
while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
i = 0;
rewind(fp);
while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
if(stricmp(buf,tbuf) == 0) {
i = 1;
break;
}
pwait(NULL);
}
if(i == 0) { /* not found, get the article */
if(getarticle(s,tbuf) == -1) {
log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
if (nntptrace >= 2)
tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
fclose(fp);
rmlock(Newsdir, "history");
fclose(tmpf);
goto quit;
}
fprintf(fp,"%s",tbuf); /* add the new message id */
}
}
fclose(fp);
rmlock(Newsdir, "history");
fclose(tmpf);
if (nntptrace >= 3)
tprintf("==>QUIT\n");
usprintf(s,"QUIT\n");
/* Eat the response */
getreply(s);
/* NOW, update the nntp.dat file */
if (mlock(Newsdir, "nntp")) {
if (nntptrace >= 2)
tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
goto quit;
}
sprintf(buf,"%s/nntp.dat",Newsdir);
fp = fopen(buf,READ_TEXT);
sprintf(buf, "%s/nntp.tmp",Newsdir);
if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
if (nntptrace >= 1)
tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
if (fp == NULLFILE || tmpf == NULLFILE) {
log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
if (nntptrace >= 2)
tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
if (fp)
fclose(fp);
if (tmpf)
fclose(tmpf);
rmlock(Newsdir, "nntp");
goto quit;
}
while (fgets(tbuf, sizeof(tbuf), fp))
if (strnicmp(tbuf, np->name, strlen(np->name)))
fputs(tbuf, tmpf);
fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
fclose(fp);
fclose(tmpf);
sprintf(buf, "%s/nntp.dat", Newsdir);
sprintf(tbuf, "%s/nntp.tmp", Newsdir);
unlink(buf);
rename(tbuf, buf);
rmlock(Newsdir, "nntp");
quit:
if (nntptrace >= 3)
tprintf("NNTP daemon exiting\n");
close_s(s);
/* Restart timer */
start_timer(&np->nntpcli_t);
return;
}
static int
gettxt(s,fp)
int s;
FILE *fp;
{
char buf[NNTPMAXLEN];
int nlines;
for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
if (nntptrace >= 4)
tprintf("<==%s", buf);
if(strcmp(buf,".\n") == 0) {
if (nntptrace >= 3)
tprintf("NNTP received %d lines\n", nlines);
return 0;
}
/* check for escaped '.' characters */
if(strcmp(buf,"..\n") == 0)
fputs(".\n",fp);
else
fputs(buf,fp);
}
if (nntptrace >= 1)
tprintf("NNTP receive error after %d lines\n", nlines);
return -1;
}
static int
getreply(s)
int s;
{
char buf[NNTPMAXLEN];
int response;
while(recvline(s,buf,NNTPMAXLEN) != -1) {
/* skip informative messages and blank lines */
if(buf[0] == '\0' || buf[0] == '1')
continue;
sscanf(buf,"%d",&response);
if (nntptrace >= 3)
tprintf("<==%s\n", buf);
return response;
}
if (nntptrace >= 3)
tprintf("==No response\n");
return -1;
}
static int
getarticle(s,msgid)
int s;
char *msgid;
{
char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
FILE *fp, *tmpf;
int r;
char *cp;
extern int Smtpquiet;
if (nntptrace >= 3)
tprintf("==>ARTICLE %s", msgid);
usprintf(s,"ARTICLE %s", msgid);
r = getreply(s);
if(r == -1 || r >= 500)
return -1;
if(r >= 400)
return 0;
if((tmpf = tmpfile()) == NULLFILE) {
if (nntptrace >= 1)
tprintf("NNTP Cannot open temp file for article\n");
return -1;
}
if(gettxt(s,tmpf) == -1) {
fclose(tmpf);
return -1;
}
/* convert the article into mail format */
rewind(tmpf);
froml[0] = '\0';
newgl[0] = '\0';
while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
if(strncmp(buf,"From: ",6) == 0) {
struct timeb t;
ftime(&t);
rip(&buf[6]);
sprintf(froml,"From %s %ld\n",&buf[6], t.time);
if(newgl[0] != '\0')
break;
}
if(strncmp(buf,"Newsgroups: ",12) == 0) {
strcpy(newgl,&buf[12]);
if(froml[0] != '\0')
break;
}
/* invalid article - missing 'From:' line or 'Newsgroups:' line */
if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
/* fclose(fp); */
fclose(tmpf);
return 0;
}
}
sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
for(cp=newgl;;++cp) {
if(*cp == '.') {
#ifdef __TURBOC__
mkdir(buf); /* create a subdirectory, if necessary */
#else
mkdir(buf,0755); /* create a subdirectory, if necessary */
#endif
strcat(buf,"/");
continue;
}
if(*cp == ',' || *cp == '\n') {
char tempdir[80], prefix[20], *p;
strcpy(tempdir, buf);
if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
*p++ = '\0';
strcpy(prefix, p);
}
if (mlock(tempdir, prefix)) {
if (nntptrace >= 2)
tprintf("NNTP group '%s' is locked\n", buf);
return -1;
}
strcat(buf,".txt");
/* open the mail file */
if (nntptrace >= 3)
tprintf("Writing article to '%s'\n", buf);
if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
fputs(froml,fp);
rewind(tmpf);
while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
/* for UNIX mail compatiblity */
if(strncmp(buf,"From ",5) == 0)
putc('>',fp);
fputs(buf,fp);
}
putc('\n',fp);
fclose(fp);
}
rmlock(tempdir, prefix);
if (*cp == '\n')
break;
else
sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
continue;
}
buf[strlen(buf)+1] = '\0';
buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
}
fclose(tmpf);
strcpy(buf,msgid); /* Get a copy we can munge */
rip(buf); /* remove trailing new-line */
rip(newgl); /* ditto */
if(!nntpquiet)
tprintf("New news arrived: %s, article %s%c\n",newgl,buf,Smtpquiet?' ':'\007');
return 0;
}